﻿using System;
using System.Web.Mvc;

namespace rossisdead.Mvc.Detergent
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class CascadingAttribute : CustomModelMetadataAttribute
    {
        #region Consts

        internal const string METADATA_KEY = "CascadingAttributeData";

        #endregion

        #region Fields

        private bool? _async;
        private static string _templateHint = "CascadingDropDown";

        #endregion

        #region Properties

        #region Static

        /// <summary>
        /// Gets/sets whether all CascadingAttributes can or can't use async requests. Default is true.
        /// </summary>
        public static bool EnableAsync { get; set; }

        /// <summary>
        /// The default UITemplateHint value set for a model if one isn't already set.
        /// Defaults to "CascadingDropDown".
        /// </summary>
        public static string TemplateHint 
        {
            get { return _templateHint; }
            set { _templateHint = value; }
        }

        #endregion


        // TODO: The Action will need to have the ~/ removed for client side.
        /// <summary>
        /// The path to the controller action that's called when the parent's value is changed.
        /// </summary>
        public string Action { get; set; }

        /// <summary>
        /// Gets/sets this individual CascadingAttribute's async. If EnableAsync is false,
        /// then this will also return false.
        /// </summary>
        public bool? Async
        {
            get
            {
                if (!EnableAsync) { return false; }
                return _async.GetValueOrDefault(EnableAsync);
            }
            set { _async = value; }
        }

        /// <summary>
        /// The name of the parent property this cascading drop down depends on. 
        /// </summary>
        public string DependsOn { get; set; }

        /// <summary>
        /// Gets the error text that should be displayed. If no value is set, defaults to the script default.
        /// </summary>
        public string ErrorText { get; set; }

        /// <summary>
        /// Gets the http method the ajax request should make. If no value is set, defaults to the script default.
        /// </summary>
        public string HttpMethod { get; set; }

        /// <summary>
        /// Gets the loading text that should be displayed. If no value is set, defaults to the script default.
        /// </summary>
        public string LoadingText { get; set; }

        /// <summary>
        /// Gets the prompt text that should be displayed. If no value is set, defaults to the script default.
        /// </summary>
        public string PromptText { get; set; }

        /// <summary>
        /// Gets the name of the property that this attribute is applied to. This value is set when
        /// the Process method is called.
        /// </summary>
        public string OwningProperty { get; private set; }

        #endregion

        #region Constructors

        static CascadingAttribute()
        {
            EnableAsync = true;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Throws an exception if any of the properties are not set.
        /// </summary>
        private void EnsurePropertiesAreSet()
        {
            if(string.IsNullOrEmpty(DependsOn))
            {
                throw new ArgumentNullException("DependsOn");
            }
            if (string.IsNullOrWhiteSpace(Action))
            {
                throw new ArgumentNullException("Action");
            }
        }

        #endregion

        #region Public methods

        public override void Process(ModelMetadata meta)
        {
            EnsurePropertiesAreSet();
            OwningProperty = meta.PropertyName;
            meta.AdditionalValues.Add(METADATA_KEY, this);
            meta.TemplateHint = TemplateHint;
        }

        /// <summary>
        /// Returns the HtmlAttributes for the given ModelMetadata. An exception will be thrown if
        /// the metadata does not include information from CascadingAttribute.
        /// </summary>
        public static CascadingAttribute GetAttributeForModel(ModelMetadata meta)
        {
            if (meta == null)
            {
                throw new ArgumentNullException("meta");
            }
        
            var values = meta.AdditionalValues[METADATA_KEY] as CascadingAttribute;
            if (values == null)
            {
                throw new InvalidOperationException(
                    "Metadata does not include cascading attribute information. Are you sure your model has a CascadingAttribute?");
            }
            return values;
        }

        #endregion

    }
}